﻿namespace NKernel.Core.Impl
{
    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;

    using log4net;

    /// <summary>
    /// Kernel decorator for adding priority features to component management.
    /// </summary>
    internal class PriorityKernelDecorator : AbstractKernel
    {
        #region Fields

        /// <summary>
        /// Decorated instance
        /// </summary>
        private readonly IKernel kernel;

        /// <summary>
        /// Logging instance
        /// </summary>
        private readonly ILog logger = LogManager.GetLogger(typeof(PriorityKernelDecorator));

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PriorityKernelDecorator"/> class.
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        public PriorityKernelDecorator(IKernel kernel)
        {
            this.kernel = kernel;
            InitPriorityList(KernelProperties.PriorityList);
        }

        #endregion Constructors

        #region Methods

        /// <summary>
        /// Cleanups this instance with components with priority status being cleaned up last.
        /// </summary>
        /// <returns></returns>
        public override KernelResult Cleanup()
        {
            logger.Info("Priority cleaning up...");

            //Put priority components into stasis
            foreach (var componentObject in kernel.GetComponents())
            {
                foreach (var priorityComponent in PriorityComponents)
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    componentObject.State = ComponentStateType.Stasis;
                    break;
                }
            }
            //Clean up the rest of the components first
            KernelResult result = kernel.Cleanup();

            //Put priority components into started state
            foreach (var componentObject in kernel.GetComponents())
            {
                foreach (var priorityComponent in PriorityComponents)
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    componentObject.State = ComponentStateType.Stopped;
                    break;
                }
            }

            //Reverse the priority before reversing again before the end of this method
            Array.Reverse(PriorityComponents);

            foreach (var priorityComponent in PriorityComponents)
            {
                foreach (var componentObject in kernel.GetComponents())
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    if (logger.IsDebugEnabled)
                    {
                        logger.DebugFormat("(match for '{0}' found in Priority Cleanup)", priorityComponent);
                    }

                    ComponentLifeCycleHandler.Cleanup(componentObject);
                    break;
                }
            }
            //Putting it back to its original order
            Array.Reverse(PriorityComponents);
            return result;
        }

        public override IList<IComponentObject> GetComponents()
        {
            return kernel.GetComponents();
        }

        public override T GetInstance<T>()
        {
            return kernel.GetInstance<T>();
        }

        public override KernelResult Init()
        {
            logger.Info("Performing a priority initialisation");
            foreach (var priorityComponent in PriorityComponents)
            {
                foreach (var componentObject in kernel.GetComponents())
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    if (logger.IsDebugEnabled)
                    {
                        logger.DebugFormat("(match for '{0}' found in Priority Init)", priorityComponent);
                    }

                    ComponentLifeCycleHandler.Init(componentObject);
                    break;
                }
            }

            return kernel.Init();
        }

        public override KernelResult Load(string[] componentLocations)
        {
            logger.Info("Delegating the load feature to the wrapped Kernel instance...");
            return kernel.Load(componentLocations);
        }

        public override KernelResult Load()
        {
            return kernel.Load();
        }

        public override KernelResult Manage()
        {
            return kernel.Manage();
        }

        public override KernelResult Manage(string[] componentLocations)
        {
            logger.Info("Priority management of full lifecycle for components located at " + string.Join(
                                                                                                 ", ", componentLocations));

            var result = Load(componentLocations);

            if (result.Success)
            {
                KernelResult resultInit = Init();
                if (resultInit.Success)
                {
                    return Start();
                }
                return new KernelResult(false, "Problem initialising the components");
            }

            return new KernelResult(false, "Failure to load the components at the indicated locations");
        }

        public override KernelResult Start()
        {
            logger.Info("Priority starting...");

            foreach (var priorityComponent in PriorityComponents)
            {
                foreach (var componentObject in kernel.GetComponents())
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    if (logger.IsDebugEnabled)
                    {
                        logger.DebugFormat("(match for '{0}' found in Priority Start)", priorityComponent);
                    }

                    ComponentLifeCycleHandler.Start(componentObject);
                    break;
                }
            }
            return kernel.Start();
        }

        public override KernelResult Stop()
        {
            logger.Info("Priority stopping...");

            //Put the priority components into stasis so that it does not get processed for stopping
            foreach (var componentObject in kernel.GetComponents())
            {
                foreach (var priorityComponent in PriorityComponents)
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }
                    componentObject.State = ComponentStateType.Stasis;
                    break;
                }
            }

            //Stopping the rest of the components...
            var result = kernel.Stop();

            //Pull the priority components out of stasis and as Started state
            foreach (var componentObject in kernel.GetComponents())
            {
                foreach (var priorityComponent in PriorityComponents)
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }
                    componentObject.State = ComponentStateType.Started;
                    break;
                }
            }

            //Reverse the priority before reversing again before the end of this method
            Array.Reverse(PriorityComponents);

            foreach (var priorityComponent in PriorityComponents)
            {
                foreach (var componentObject in kernel.GetComponents())
                {
                    if (!Regex.IsMatch(componentObject.ComponentName, priorityComponent, RegexOptions.IgnoreCase))
                    {
                        continue;
                    }

                    if (logger.IsDebugEnabled)
                    {
                        logger.DebugFormat("(match for '{0}' found in Priority Stop)", priorityComponent);
                    }

                    ComponentLifeCycleHandler.Stop(componentObject);
                    break;
                }
            }

            //Put back the original order of the priority components
            Array.Reverse(PriorityComponents);

            return result;
        }

        #endregion Methods
    }
}